/**
 * 1.Promise 是一个 class，实例化时需传入一个函数（参数为 resolve、reject），该函数会立即执行。如果执行出错，reject(error)。
 * 2.Promise 的三种状态，Pending/FULFILLED/Rejected，状态的改变由 resolve/reject 函数完成，改变后不可逆转。
 *      resolve 函数，Pending > FULFILLED
 *      reject 函数，Pending > Rejected
 * 3-1.then 方法，接受两个参数，分别是成功和失败的回调。根据当前状态执行相应的回调。
 *      两个参数为可选参数，默认值分别为 v=>v、e=>{throw e}，将值或失败理由向后传递；
 *      两个回调参数如果执行出错，reject(error)
 * 3-2.then 调用时如果状态为 pending（异步操作后才改变状态），需将回调保存，在 resolve 或 reject 执行时执行回调。
 * 3-3.同一个 promise 对象的 then 方法，可以多次调用。（同步的正常，异步的 cb 需要使用数组存储，等到状态改变时所有 cb 依次执行）。
 * 3-4.then 方法可链式调用，下一个 then 方法拿到的是当前 then 方法 return 的值：
 *      return 当前 promise，循环引用是不被允许的（需放到 setTimeout 中做一个延迟执行，才能拿到当前 promise）；
 *      return 新的 promise，调用该 promise 的 then 方法即可
 *      return 其他非 promise 的值，resolve(v)即可
 * 4.catch 方法，then 方法，第一个参数为 undefined
 * 5.finally 方法，接收一个普通的回调函数（不管 promise 最终失败还是成功都会执行该回调），返回 promise（传递 value/reason）
 * 6.done 方法，位于回调链尾端，抛出任何可能的错误
 * 7.静态方法 all/race，接收一个具有 Iterator 接口的参数，如果参数项不是 promise，会先用 Promise.resolve 包装。
 *      all 所有都 resolve 则返回 resolve 的值组成的有序数组，任何一个 reject 则 reject。
 *      race 最先改变状态的一项，决定了该 promise 的最后状态。
 *      any(ES2021) 一个 resolve 包装实例 resolve，所有 reject 则 reject，抛出的错误是 AggregateError 实例（多个错误包装在一个错误内）
 *      allSettled(ES2020)，所有实例都返回结果后，包装实例 resolve，返回的结果是一个数组，数组每一项是状态和返回值构成的对象
 * 8.静态方法 resolve，接收一个参数，返回 promise
 *      参数为 promise，原样返回
 *      参数为 thenable 的对象，包装成 promise，并立即执行 then 方法
 *      参数为其他对象或基础类型值，resolve 该参数
 * 9.静态方法 reject，接受一个参数，返回 Rejected 状态的 promise，reason 为接受的参数（原封不动）。
 */
const PENDING = "Pending";
const FULFILLED = "Fulfilled";
const REJECTED = "Rejected";

class MyPromise {
  status = PENDING;
  value;
  reason;
  successCallbacks = [];
  failCallbacks = [];

  constructor(executor) {
    if (typeof executor !== "function")
      throw new TypeError("Promise resolver is not a function at new Promise");
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  resolve = (value) => {
    if (this.status !== PENDING) return;
    this.status = FULFILLED;
    this.value = value;
    this.successCallbacks.forEach((cb) => cb());
  };

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    this.failCallbacks.forEach((cb) => cb());
  };

  then(successCallback, failCallback) {
    successCallback = successCallback || ((value) => value);
    failCallback =
      failCallback ||
      ((reason) => {
        throw reason;
      });
    const p = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // setTimeout 不能提取到 handleThenCallback 中，不然就拿不到 p
        setTimeout(() => {
          handleThenCallback(p, successCallback, this.value, resolve, reject);
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          handleThenCallback(p, failCallback, this.reason, resolve, reject);
        }, 0);
      } else {
        this.successCallbacks.push(() => {
          setTimeout(() => {
            handleThenCallback(p, successCallback, this.value, resolve, reject);
          }, 0);
        });
        this.failCallbacks.push(() => {
          setTimeout(() => {
            handleThenCallback(p, failCallback, this.reason, resolve, reject);
          }, 0);
        });
      }
    });
    return p;
  }

  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  finally(callback) {
    return this.then(
      (value) => MyPromise.resolve(callback()).then(() => value),
      (reason) =>
        MyPromise.resolve(callback()).then(() => {
          throw reason;
        })
    );
  }

  // done(successCallback, failCallback) {
  //     this.then(successCallback, failCallback).catch(reason => {
  //         // 抛出一个全局的错误
  //         setTimeout(() => {
  //             throw reason
  //         }, 0);
  //     });
  // }

  static all(array) {
    const real_array = iterator2Array(array);
    return new MyPromise((resolve, reject) => {
      let result = [],
        len = real_array.length;
      const addDataToResult = (index, value) => {
        result[index] = value;
        if (--len <= 0) resolve(result);
      };
      real_array.forEach((item, index) => {
        item = item instanceof MyPromise ? item : MyPromise.resolve(item);
        item.then(
          (value) => addDataToResult(index, value),
          (reason) => reject(reason)
        );
      });
    });
  }

  static race(array) {
    const real_array = iterator2Array(array);
    return new MyPromise((resolve, reject) => {
      real_array.forEach((item, index) => {
        item = item instanceof MyPromise ? item : MyPromise.resolve(item);
        item.then(
          (value) => resolve(value),
          (reason) => reject(reason)
        );
      });
    });
  }

  static any(array) {
    const real_array = iterator2Array(array);
    return new MyPromise((resolve, reject) => {
      let len = real_array.length;
      const reasons = [];
      const addDataToErr = (index, reason) => {
        reasons[index] = reason;
        if (--len <= 0) {
          const err = new AggregateError(reasons, "All promises were rejected");
          reject(err);
        }
      };
      real_array.forEach((item, index) => {
        item = item instanceof MyPromise ? item : MyPromise.resolve(item);
        item.then(
          (value) => resolve(value),
          (reason) => addDataToErr(index, reason)
        );
      });
    });
  }

  static allSettled(array) {
    const real_array = iterator2Array(array);
    return new MyPromise((resolve, reject) => {
      let result = [],
        len = real_array.length;
      const addDataToResult = (index, value) => {
        result[index] = value;
        if (--len <= 0) resolve(result);
      };
      real_array.forEach((item, index) => {
        item = item instanceof MyPromise ? item : MyPromise.resolve(item);
        item.then(
          (value) => addDataToResult(index, { status: "fulfilled", value }),
          (reason) => addDataToResult(index, { status: "rejected", reason })
        );
      });
    });
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value;
    else if (value && typeof value.then === "function") {
      // thenable 的对象，该如何判断 then 方法是 thenable 呢？
      // let thenable = {
      //     then: (resolve, reject) => resolve(42)
      // }
    } else {
      return new MyPromise((resolve, reject) => {
        resolve(value);
      });
    }
  }

  static reject(value) {
    return new MyPromise((resolve, reject) => {
      reject(value);
    });
  }
}

/**
 * then 方法失败/成功回调的执行、处理
 * @param {*} p then 方法返回的新 promise
 * @param {*} callback then 方法传入的成功/失败回调函数
 * @param {*} data resolve 的 value，或 reject 的 reason
 * @param {*} resolve
 * @param {*} reject
 */
function handleThenCallback(p, callback, data, resolve, reject) {
  try {
    const v = callback(data);
    handleThenCallbackReturnValue(p, v, resolve, reject);
  } catch (error) {
    reject(error);
  }
}
/**
 * 根据 then 方法 成功/失败回调返回值，改变新 promise 的状态
 * @param {*} p then 方法返回的新 promise
 * @param {*} v 成功或失败回调执行后返回的值
 * @param {*} resolve
 * @param {*} reject
 */
function handleThenCallbackReturnValue(p, v, resolve, reject) {
  if (p === v) {
    throw new TypeError("Chaining cycle detected for promise #<Promise>");
  }
  if (v instanceof MyPromise) v.then(resolve, reject);
  else resolve(v);
}

/**
 * 将具有 Iterator 接口转化为数组，如果参数不具有 Iterator 接口，抛出错误
 * @param {*} v 待转化的参数
 */
function iterator2Array(v) {
  let real_array = [];
  try {
    for (let i of v) real_array.push(i);
  } catch (e) {
    throw new Error(e.message);
  }
  return real_array;
}

export default MyPromise;
